package org.jeecg.common.aspect;


import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.LimitSubmit;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 防止重复提交aop类
 */
@Aspect
@Component
@Slf4j
public class LimitSubmitAspect {

    //封装了redis操作各种方法
    @Autowired
    private RedisUtil redisUtil;

    @Pointcut("@annotation(org.jeecg.common.aspect.annotation.LimitSubmit)")
    private void limitSubmit() {}


    @Around("limitSubmit()")
    public Object handleSubmit(ProceedingJoinPoint joinPoint) throws Throwable {
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //获取注解信息
        LimitSubmit limitSubmit = method.getAnnotation(LimitSubmit.class);
        int submitTimeLimiter = limitSubmit.limit();
        String redisKey = limitSubmit.key();
        String[] spelKey = limitSubmit.spelKey();
        boolean needAllWait = limitSubmit.needAllWait();
        String key =  getRedisKey(loginUser,joinPoint, redisKey, spelKey);
        Object result = redisUtil.get(key);
        if (result != null) {
            return Result.error("请勿重复提交！");
        }
        redisUtil.set(key, loginUser.getId(), submitTimeLimiter);
        try {
            Object proceed = joinPoint.proceed();
            return proceed;
        } catch (Throwable e) {
            log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(),
                    joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL", e.getMessage(), e);
            throw e;
        }finally {
            if(!needAllWait) {
                redisUtil.del(redisKey);
            }
        }
    }

    /**
     * 支持多参数，从请求参数进行处理
     */
    private String  getRedisKey(LoginUser loginUser, ProceedingJoinPoint joinPoint , String key, String[] spelKey){
        /**处理常量key*/
        if(key.contains("%uId")) {//当前登录用户ID
            key = key.replaceAll("%uId", loginUser.getId());
        }
        if(key.contains("%bId")) {//当前登录商户ID
            key = key.replaceAll("%bId", oConvertUtils.getString(loginUser.getCurBusiness(),"0"));
        }
        if(key.contains("%cId")) {//当前登录服务公司ID
            key = key.replaceAll("%cId", oConvertUtils.getString(loginUser.getCurCompany(), "0"));
        }
        if(key.contains("%tId")) {//当前登录租户ID
            key = key.replaceAll("%tId", oConvertUtils.getString(loginUser.getRelTenantIds(), "0"));
        }

        /**处理sepl key*/
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = discoverer.getParameterNames(method);
        Object[] args = joinPoint.getArgs();
        for (String spel : spelKey) {
            String valueBySpEL = getValueBySpEL(spel, parameterNames, args);
            key += valueBySpEL;
        }
        System.out.println("==防止重复提交key==:" + key);
        return key;
    }

    /**
     * 通过spring SpEL 获取参数
     *
     * @param key            定义的key值 以#开头 例如:#user
     * @param parameterNames 形参
     * @param values         形参值
     * @return
     */
    public String getValueBySpEL(String key, String[] parameterNames, Object[] values) {
        List<String> keys = new ArrayList<>();
        if (!key.contains("#")) {
            return ":" + key;
        }
        //spel解析器
        ExpressionParser parser = new SpelExpressionParser();
        //spel上下文
        EvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], values[i]);
        }
        Expression expression = parser.parseExpression(key);
        Object value = expression.getValue(context);
        if (value != null) {
            if (value instanceof List) {
                List value1 = (List) value;
                for (Object o : value1) {
                    keys.add(o.toString());
                }
            } else if (value.getClass().isArray()) {
                Object[] obj = (Object[]) value;
                for (Object o : obj) {
                    keys.add(o.toString());
                }
            } else {
                keys.add(value.toString());
            }
        }
        String str = "";
        for (String s : keys) {
            str += ":" + s;
        }
        return str;
    }

}
